package com.qunar.toku.loadbalance;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.jboss.netty.logging.InternalLogger;
import org.jboss.netty.logging.InternalLoggerFactory;

import com.qunar.toku.configuration.ServerProfile;
import com.qunar.toku.configuration.ServiceConfig;
import com.qunar.toku.handler.ClientHandler;
import com.qunar.toku.loadbalance.component.ServerState;
import com.qunar.toku.socket.SocketPool;

public class Dispatcher {
	private final InternalLogger logger = 
			InternalLoggerFactory.getInstance(Dispatcher.class);
	private List<Server> serverPool = new ArrayList<Server>();
	private AtomicInteger requestCount = new AtomicInteger(0);

	public Dispatcher(ServiceConfig config) {
		logger.info("starting init servers");
        logger.debug("init connection begin:" + System.currentTimeMillis());
		for (ServerProfile ser : config.getServers()) {
			if (ser.getWeightRate() > 0) {
				Server server = new Server(ser);
				SocketPool sp = new SocketPool(ClientHandler.class, config.getSocketPool(), server);
				server.setSocketPool(sp);
				serverPool.add(server);
			}
		}
		logger.debug("init connection end:" + System.currentTimeMillis());
        logger.info("init servers end");
	}

	public Server getServer() {
		if (serverPool == null || serverPool.size() == 0) {
            return null;
        }

        Server result = null;
        int currUserCount = -1;
        int count = serverPool.size();
        int start = requestCount.get() % count;
        if (requestCount.get() > 10) {
            requestCount.set(0);
        } else {
            requestCount.getAndIncrement();
        }

        for (int i = start; i < start + count; i++) {
            int index = i % count;
            Server server = serverPool.get(index);
  
            if (server.getState() == ServerState.Dead && (System.currentTimeMillis() - server.getDeadTime()) > server.getDeadTimeout()) {
                synchronized (this) {
                    if (server.getState() == ServerState.Dead && (System.currentTimeMillis() - server.getDeadTime()) > server.getDeadTimeout()) {
                        result = server;
                        server.setState(ServerState.Testing);
                        server.setDeadTime(0);
                        logger.warn("find server that need to test!host:" + server.getAddress());
                        break;
                    }
                }
            }
            
            if(server.getState() == ServerState.Reboot && (System.currentTimeMillis() - server.getDeadTime()) > server.getDeadTimeout()){
       		 	synchronized (this) {
                    if (server.getState() == ServerState.Reboot && (System.currentTimeMillis() - server.getDeadTime()) > server.getDeadTimeout()) {
                    	result = server;
	                   	server.setState(ServerState.Testing);
                        server.setDeadTime(0);
                        requestCount.getAndDecrement();
                        logger.warn("find server that need to test!host:" + server.getAddress());
                        break;
                    }
                }
            }
          
            if ((server.getCurrUserCount() < currUserCount * server.getWeightRage() || currUserCount < 0)
                    && server.getState() == ServerState.Normal) {
                currUserCount = server.getCurrUserCount();
                result = server;
            }
        }

        if (result == null) {
        	result = serverPool.get(new SecureRandom().nextInt(count));
        }
        
        return result;

	}
	
	public List<Server> getAllServer() {
    	return serverPool;
    }
}
